package com.ruoyi.common.utils.obs;

import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.AccessControlList;
import com.obs.services.model.PutObjectRequest;
import com.obs.services.model.PutObjectResult;
import com.ruoyi.common.config.ObsProperties;
import com.ruoyi.common.utils.UUIDUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Component
public class ObsService {

  private static final String EXTENSION_SEPARATOR = ".";
  private static final DateTimeFormatter DATE_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy/MM/dd");

  @Autowired
  private ObsProperties properties;
  private volatile ObsClient obsClient; // 单利模式客户端

  // 线程安全的获取ObsClient实例
  private ObsClient getInstance() {
    if (obsClient == null) {
      synchronized (this) {
        if (obsClient == null) {
          obsClient = new ObsClient(
              properties.getAccessKey(),
              properties.getSecretKey(),
              properties.getEndpoint()
          );
        }
      }
    }
    return obsClient;
  }

  /**
   * 通用文件上传方法
   *
   * @param file   待上传文件
   * @param module 业务模块（如user-avatar, product-image等）
   * @return 文件访问URL
   */
  public String uploadFile(MultipartFile file, String module) throws IOException {

    if (ObjectUtils.isEmpty(file)) {
      throw new IllegalArgumentException("上传文件不能为空");
    }

    String originalFilename = file.getOriginalFilename();
    String objectKey = StringUtils.isNotEmpty(module) ? generateObjectKey(originalFilename, module) : generateObjectKey(originalFilename);

    // 执行上传
    try (InputStream inputStream = file.getInputStream()) {

      PutObjectRequest request = new PutObjectRequest(
          properties.getBucketName(),
          objectKey,
          inputStream
      );

      // 设置ACL为公共读
      request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);

      PutObjectResult result = getInstance().putObject(request);

      // 验证上传结果
      if (result == null || result.getStatusCode() != 200) {
        String errorMsg = String.format(
            "OBS文件上传失败：状态码=%d, 文件=%s",
            result.getStatusCode(), originalFilename
        );
        throw new IOException(errorMsg);
      }

      return buildFileUrl(objectKey);
    } catch (ObsException e) {
      String errorMsg = String.format(
          "OBS服务异常: code=%s, msg=%s, file=%s",
          e.getErrorCode(), e.getErrorMessage(), originalFilename
      );
      throw new IOException(errorMsg, e);
    }

  }

  /**
   * 构建文件访问URL
   *
   * @param objectKey OBS存储路径
   * @return 完整的访问URL
   */
  private String buildFileUrl(String objectKey) {
    return String.format("https://%s.%s/%s",
        properties.getBucketName(),
        properties.getEndpoint(),
        objectKey);
  }

  /**
   * 生成存储路径：module/yyyy/MM/dd/UUID.扩展名
   *
   * @param originalFilename 原始文件名
   * @param module           业务模块
   * @return 完整存储路径
   */
  private String generateObjectKey(String originalFilename, String module) {

    // 日期路径
    String datePath = LocalDate.now().format(DATE_FORMATTER);

    // 生成唯一文件名
    String uuid = UUIDUtils.getUUID();

    // 获取文件扩展名
    String extension = getFileExtension(originalFilename);

    // 构建完整路径
    return String.format("%s/%s/%s%s",
        module, datePath, uuid, extension);

  }

  private String generateObjectKey(String originalFilename) {
    return generateObjectKey(originalFilename, "default");
  }

  /**
   * 获取文件扩展名（包含点）
   *
   * @param filename 原始文件名
   * @return 文件扩展名，如".jpg"，如果无扩展名则返回空字符串
   */
  private String getFileExtension(String filename) {
    if (StringUtils.isBlank(filename)) {
      return "";
    }

    int dotIndex = filename.lastIndexOf(EXTENSION_SEPARATOR);
    if (dotIndex > 0 && dotIndex < filename.length() - 1) {
      return filename.substring(dotIndex).toLowerCase();
    }
    return "";
  }


}
